/*
	Stargate SENT for GarrysMod10
	Copyright (C) 2007  aVoN

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
--################# HEADER #################

--Include
AddCSLuaFile("cl_init.lua");
AddCSLuaFile("shared.lua");

include("shared.lua");
include("modules/lib_tcs.lua");
include("modules/events_tcs.lua");

-- Defines
ENT.CDSIgnore = true; -- CDS Immunity
function ENT:gcbt_breakactions() end; ENT.hasdamagecase = true; -- GCombat invulnarability!

-- Offsets for "InRamp"-Spawning
ENT.RampOffset = {
	["models/Zup/ramps/sgc_ramp.mdl"] = Vector(0,0,0),
	["models/Zup/ramps/brick_01.mdl"] = Vector(0,0,-10),
}

--################# SENT CODE #################
--################# Initialize
function ENT:Initialize()
	--################# Config
	self.DHDRange = StarGate.CFG:Get("dhd","range",1000); -- Maximum range a DHD can be in before the gate says "Hey, you do not belong to me, get the fuck out"
	--################# General defines and inits
	self:RegisterSequenceTable(); -- Register a "Copy" of the self.Sequence table, so out sequence won't be mixed between Atlantis/SG1 gates
	self.GatePrivat = self.GatePrivat or false; -- Is the gate Private?
	self.GateName = self.GateName or ""; -- The name of this gate
	self.GateAddress = self.GateAddress or ""; -- Our Address
	self.DialledAddress = {}; -- Still used for compatibility reasons
	self.IsOpen = false; -- Still used for compatibility reasons
	self.Dialling = false; -- Still used for compatibility reasons
	self.DialledAll = false; -- To indicate last hacked dial
	self.HighEnergyDial = false; -- If this is true, gate gets activated intantly, only energy gates.
	self.IsEnergyGate = true;
	self.DialType = {};
	self.Entity:SetUseType(SIMPLE_USE);
	--################# Wire!
	self:CreateWireInputs("Dial Address","Dial Mode","Close","Disable Autoclose", "Hack Dial");
	self:CreateWireOutputs("Active","Open","Inbound","Chevron", "dhdenergy", "energy", "max energy", "energy need");
	self:AddResource("energy",100000);
	self:AddResource("dhdenergy",100000000);
	self:SupplyResource("energy",10000);
	self.energy = self:GetResource("energy", 0) or 0;
	self.needenergy = 10;
	self.dhdenergy = self:GetResource("dhdenergy", 0) or 0;
	self.energycusumeopen = 200 -- per second. The consume amount depends on the distance of the gate and dial mode.
	self.energycusumedial = 100 --For each chevron this amount. Chevron 6 *2 Chevron 7 *3 etc.
	self.energymultiply = 1 --Use this for distance
	self:SetWire("max energy",100000);
	
	--################# Fix for Duplicator, or the ring and chevron will look strange when loading a saved gate
	local ang = self.Entity:GetAngles();
	if(ang ~= Angle(0,0,0)) then
		self.Entity:SetAngles(Angle(0,0,0));
		local e = self.Entity;
		timer.Simple(0,
			function()
				if(ang and ValidEntity(e)) then
					e:SetAngles(ang);
				end
			end
		);
	end
	--################# Set physic and entity properties
	local phys = self.Entity:GetPhysicsObject();
	if(phys:IsValid()) then
		phys:EnableMotion(false);
		phys:SetMass(32000); -- A stargate weights 32 tons!
	end
end

--################# For the spawnfunction, to put a gate directly into ramps @aVoN
function ENT:PutIntoRamps(t)
	local e = t.Entity;
	if(not ValidEntity(e)) then return end;
	local mdl = e:GetModel();
	if(self.RampOffset[mdl]) then
		-- Check, if there is already a gate snapped to it...
		for _,v in pairs(StarGate.GetConstrainedEnts(e,2) or {}) do
			if(ValidEntity(v) and v:GetClass():find("stargate_")) then return end;
		end
		-- Freeze ramp
		local phys = e:GetPhysicsObject();
		if(phys:IsValid()) then
			phys:EnableMotion(false);
		end
		self.Entity:SetAngles(e:GetAngles());
		self.Entity:SetPos(e:LocalToWorld(self.RampOffset[mdl]));
		constraint.Weld(self.Entity,e,0,0,0,true);
		e.CDSIgnore = true; -- Fixes Combat Damage System destroying Ramps - http://mantis.39051.vs.webtropia.com/view.php?id=45
		return e;
	end
end

--#################  When getting removed..
function ENT:OnRemove()
	self:Close(); -- Close the horizon
	self:StopActions(); -- Stop all actions and sounds
	self:DHDDisable(0); -- Shutdown near DHD's
	if(ValidEntity(self.Target)) then
		if(self.IsOpen) then
			self.Target:DeactivateStargate(true);
		elseif(self.Dialling) then
			self.Target:EmergencyShutdown(true);
		end
	end
end

--################# When it get's hurt, make it flicker etc
-- FIXME: Write new damage system
function ENT:OnTakeDamage(d)
	--self:UseEnergy(d:GetDamage())
end

--#################  Use - Open the Dial Menu @aVoN
function ENT:Use(p)
	if(ValidEntity(p) and p:IsPlayer()) then
		if(hook.Call("StarGate.Player.CanDialGate",GAMEMODE,p,self.Entity) == false) then return end; -- Not allowed to dial!
		local dialog = "StarGate.OpenDialMenu";
		if(hook.Call("StarGate.Player.CanModifyGate",GAMEMODE,p,self.Entity) == false) then
			dialog = "StarGate.OpenDialMenuDHD"; -- He is not allowed to modify stuff, so show him the normal dialling dialoge!
		elseif(self.GateSpawnerProtected) then -- It's a protected gate. Can this user change it?
			local allowed = hook.Call("StarGate.Player.CanModifyProtectedGate",GAMEMODE,p,self.Entity);
			if(allowed == nil) then allowed = (p:IsAdmin() or SinglePlayer()) end;
			if(not allowed) then
				dialog = "StarGate.OpenDialMenuDHD";
			end
		end
		umsg.Start(dialog,p);
		umsg.Entity(self.Entity);
		umsg.End();
		self.LastUse = time;
	end
end

function ENT:UseEnergy(use, dial) -- if dial is false, distance multiply will be calculated.	
	local mp = self.energymultiply
	if (self.DialledAll) then --Make out gate eat all the energy if it has to dial all the gates
		mp = mp +10;
		self.dhdenergy = 0	--Drains all the dhdenergy. We cant use a dhd to dial them all, maybe i'll fix this.
	end
	if (self.HighEnergyDial) then -- this needs fixing
		mp = mp + 2
	end
	if (dial) then
		--self:DHDIsEnergyActive(); --Checks if the dhd has energy to give, Minor bug to fix.
		local r = math.Round(use * mp);
		self.needenergy = r;
		if (self.dhdenergy >= r * 2) then --use 2x the energy, becuase its dhdenergy
			self:ConsumeResource("dhdenergy", r * 2); 
			return true;
		else
			if self.energy >= r then
				self:ConsumeResource("energy", r);
				return true;
			else
				self.energy = 0
				return false;	
			end
		end
	else
		local distance = 1
		local egate = false
		if ValidEntity(self.Target) then
			if(self.Target.IsStargate) then
				local pos = self.Entity:GetPos();
				local sg_dist = pos:Distance( self.Target:GetPos() ) 
				distance = (sg_dist * 0.025) -- 0.25% distance
			end
			if (self.Target.IsEnergyGate) then
				egate = true
			end
		end
		--
		local r = math.Round(use * distance * mp);
		self.needenergy = r;
		self:SetWire("energy need",r); --The energy needed to maintain the wormhole, can use generators etc.
		if (self.dhdenergy >= (r*2)) then --use 2x the energy, becuase its dhdenergy
			self:ConsumeResource("dhdenergy", r * 2); 
			return true;
		else
			if self.energy >= r then
				self:ConsumeResource("energy", r);
				return true;
			else
				if (egate) then
					if (self.Target.dhdenergy >= r * 2) then
						self.Target:ConsumeResource("dhdenergy", r * 2);
						return true;
					elseif (self.Target.energy >= r) then
						self.Target:ConsumeResource("energy", r);
						return true;
					else
						return false;
					end
				else
					self.energy = 0
					return false;
				end
			end
		end
	end

end


--##################################
--#### Wire Inputs
--##################################


--################# Wire input @aVoN
function ENT:TriggerInput(k,v)
	if(k == "Dial Address") then
		local char = string.char(v):upper();
		if(v == 13 and #self.DialledAddress == 8) then -- Enter Key
			local fast = self:GetWire("Dial Mode",0) >= 1;
			if(self.Entity:GetClass() == "stargate_atlantis") then fast = true end; -- SGA is ALWAYS dialling fast! - FIXME: Add new dialling to this gate (with the new sounds!)
			self:SetDialMode(false,fast); -- If DialMode is 0, we dial slowly, if Dial Mode is greater we dial fast
			self:StartDialling();
		elseif(v == 127) then -- Backspace key
			table.remove(self.DialledAddress);
		elseif(char:find("[A-Z0-9@]")) then -- Only alphanumerical and the @
			if(#self.DialledAddress < 6 and not table.HasValue(self.DialledAddress,char)) then
				table.insert(self.DialledAddress,char);
			end
			if(#self.DialledAddress == 6) then
				table.insert(self.DialledAddress,"#");
				table.insert(self.DialledAddress,"DIAL");
			end
		end
	elseif(k == "Close") then
		self:AbortDialling();
	elseif(k == "Hack Dial") then
		self:DialAll(v);
	end
end

--################# Wire ouput - Relay to a mobile DHD @aVoN
function ENT:WireOutput(k,v)
	for _,e in pairs(ents.FindByClass("mobile_dhd")) do
		if((e:GetPos()-self.Entity:GetPos()):Length() <= self.DHDRange) then
			e:SetWire(k,v);
		end
	end
end
